//
// Created by HP on 2022/1/31.
//

#ifndef TMC_TMC_UTILS_H
#define TMC_TMC_UTILS_H

// lib
#include <glad/glad.h>

// std
#include <functional>
#include <stdexcept>

namespace tmc {
    // https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x
    template <typename T, typename... Rest>
    void hashCombine(std::size_t & seed, const T& v, Rest... rest)
    {
        seed ^= std::hash<T>{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
        (hashCombine(seed, rest), ...);
    }

    // 用于删除一个复合结构默认的复制构造函数和赋值运算符
    #define DELETE_COPY(TYPE) \
        TYPE(const TYPE &) = delete; \
        TYPE& operator=(const TYPE &) = delete


    inline void __attribute__((__noreturn__))
    throwError(const char *file, std::size_t line, const std::string& msg) {
        throw std::runtime_error{
            "Error: In file (" + std::string(file) + ":" + std::to_string(line) + "): " + msg
        };
    }
    inline void __attribute__((__noreturn__))
    throwGLError(const char *file, std::size_t line, const std::string& msg) {
        throw std::runtime_error{
            "Error: In file (" + std::string(file) + ":" + std::to_string(line) + "): " + msg + "\n" +
            "Please check in https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/ \n"
        };
    }
    inline void handleOpenGLError(const char *file, std::size_t line) {
        GLenum code = glGetError();
        switch (code) {
            case GL_NO_ERROR:
                return;
            case GL_INVALID_ENUM:
                throwGLError(file, line, "GL_INVALID_ENUM");
            case GL_INVALID_VALUE:
                throwGLError(file, line, "GL_INVALID_VALUE");
            case GL_INVALID_OPERATION:
                throwGLError(file, line, "GL_INVALID_OPERATION");
            case GL_INVALID_FRAMEBUFFER_OPERATION:
                throwGLError(file, line, "GL_INVALID_FRAMEBUFFER_OPERATION");
            case GL_OUT_OF_MEMORY:
                throwGLError(file, line, "GL_OUT_OF_MEMORY");
            case GL_STACK_UNDERFLOW:
                throwGLError(file, line, "GL_STACK_UNDERFLOW");
            case GL_STACK_OVERFLOW:
                throwGLError(file, line, "GL_STACK_OVERFLOW");
            default:
                throwError(file, line, "UNEXPECTED_ERROR");
        }
    }

    #define TMC_THROW_ERROR(MSG) tmc::throwError(__FILE__, __LINE__, MSG)
    #define TMC_HANDLE_GL_ERROR() tmc::handleOpenGLError(__FILE__, __LINE__)
}

#endif //TMC_TMC_UTILS_H
